#ifndef PARSE_DECLARATION_H__
#define PARSE_DECLARATION_H__

#include <stdlib.h>
#include "parse-basic.h"
#include "parse-expression.h"
#include "parse-type.h"
#include "token.h"
#include "../ast/statement.h"

#if 0
import std.lib;
import parse-basic;
import parse-expression;
import parse-label;
import parse-type;
import token;
import ..ast.statement;
#endif

struct Statement* parseStatement(struct TokenList**);

struct EnumDeclarationStatement* parseEnumDeclaration(struct TokenList** source)
{
	struct TokenList* save = *source;
	struct EnumDeclaration* r = malloc(sizeof(struct EnumDeclaration));
	// TODO
	free(r);
	*source = save;
	return NULL;
}

struct ArgumentDeclaration* parseArgumentDeclaration(struct TokenList** source)
{
	// For now, type can be null and/or name can be null.
	// However, that is not valid code; error checking will be added.
	// TODO
	struct Type* type = parseType(source);
	struct Name* name = parseName(source);

	return allocateArgumentDeclaration(type,name);
}

struct ArgumentDeclarationList* parseArgumentDeclarationList(
	struct TokenList** source)
{
	struct ArgumentDeclaration* argument = parseArgumentDeclaration(source);

	if(argument == NULL) return NULL;

	if(*source == NULL || (*source)->head->tag != COMMA)
		return allocateArgumentDeclarationList(argument,NULL);

	*source = (*source)->tail;

	return allocateArgumentDeclarationList(
		argument,
		parseArgumentDeclarationList(source));
}

struct FunctionDeclaration* parseFunctionDeclaration(struct TokenList** source)
{
	struct TokenList* save = *source;

	struct Type* type = parseType(source);	// type can be null.
	struct Name* name = parseName(source);	// name can be null (for now).

	if(*source == NULL || (*source)->head->tag != OPEN_PARENTHESE)
	{
		*source = save;
		freeType(type);
		freeName(name);
		return NULL;
	}

	*source = (*source)->tail;

	struct ArgumentDeclarationList* arguments
		= parseArgumentDeclarationList(source); // argumentList can be null.

	if(*source == NULL || (*source)->head->tag != CLOSE_PARENTHESE)
	{
		*source = save;
		freeType(type);
		freeName(name);
		freeArgumentDeclarationList(arguments);
		return NULL;
	}

	*source = (*source)->tail;
	struct Statement* statement = parseStatement(source);

	if(statement == NULL)
	{
		*source = save;
		freeType(type);
		freeName(name);
		freeArgumentDeclarationList(arguments);
		return NULL;
	}

	return allocateFunctionDeclaration(type,name,arguments,statement);
}

/* TODO Combine parseStructDeclaration() and parseUnionDeclaration() into
   one parseTypeDeclaration(). */

struct StructDeclaration* parseStructDeclaration(struct TokenList** source)
{
	struct TokenList* save = *source;
	struct StructDeclarationStatement* r
		= malloc(sizeof(struct StructDeclaration));
	// TODO
	free(r);
	*source = save;
	return NULL;
}

struct UnionDeclaration* parseUnionDeclaration(struct TokenList** source)
{
	struct TokenList* save = *source;
	struct UnionDeclarationStatement* r
		= malloc(sizeof(struct UnionDeclaration));
	// TODO
	free(r);
	*source = save;
	return NULL;
}

struct VariableDeclaration* parseVariableDeclaration(
	struct TokenList** source)
{
	struct TokenList* save = *source;
	struct VariableDeclarationStatement* r
		= malloc(sizeof(struct VariableDeclaration));
	// TODO
	free(r);
	*source = save;
	return NULL;
}

struct Declaration* parseDeclaration(struct TokenList** source)
{
	void* declaration;

	// TODO Implement parseEnumDeclaration()
	//if((declaration = parseEnumDeclaration(source)))
	//	return allocateDeclaration(ENUM_DECLARATION,declaration);

	if((declaration = parseFunctionDeclaration(source)))
		return allocateDeclaration(FUNCTION_DECLARATION,declaration);

	// TODO Implement parseStructDeclaration()
	//if((declaration = parseStructDeclaration(source)))
	//	return allocateDeclaration(STRUCT_DECLARATION,declaration);

	// TODO Implement parseUnionDeclaration()
	//if((declaration = parseUnionDeclaration(source)))
	//	return allocateDeclaration(UNION_DECLARATION,declaration);

	// TODO Implement parseVariableDeclaration()
	//if((declaration = parseVariableDeclaration(source)))
	//	return allocateDeclaration(VARIABLE_DECLARATION,declaration);

	return NULL;
}

struct DeclarationList* parseDeclarationList(struct TokenList** source)
{
	struct Declaration* head = parseDeclaration(source);
	if(head == NULL) return NULL;
	return allocateDeclarationList(head,parseDeclarationList(source));
}

#endif
